package com.zwb.demo.reader;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.zwb.demo.exception.ConfigException;
import com.zwb.demo.exception.ExceptionEnum;
import com.zwb.demo.model.FieldDefinition;
import com.zwb.demo.model.SheetDefinition;
import com.zwb.demo.strategy.ICellParser;
import com.zwb.demo.utils.ApplicationContextHolder;
import com.zwb.demo.utils.MyStringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.util.*;

@Component
@Slf4j
public class ExcelParser {

    @Value("${java-package}")
    private String javaPackage;

    private final Map<String, ICellParser> cellParsersMap = new HashMap<>();

    /**
     * 注册计算器
     */
    @EventListener(ApplicationStartedEvent.class)
    public void register() {
        Map<String, ICellParser> beansOfType = ApplicationContextHolder.getCtx().getBeansOfType(ICellParser.class);
        for (ICellParser iCellParser : beansOfType.values()) {
            cellParsersMap.put(iCellParser.name(), iCellParser);
        }
    }

    public ICellParser getCellParser(String name) {
        ICellParser iCellParser = cellParsersMap.get(name);
        if (iCellParser == null) {
            iCellParser = cellParsersMap.get("string");
        }
        return iCellParser;
    }

    public List<SheetDefinition> parse(File file) throws IOException, InvalidFormatException {
        List<SheetDefinition> list = new ArrayList<>();
        String fileName = file.getName();
        log.info("parse [{}]", fileName);
        Workbook workbook = WorkbookFactory.create(file);
        int numberOfSheets = workbook.getNumberOfSheets();
        for (int i = 0; i < numberOfSheets; i++) {
            Sheet sheet = workbook.getSheetAt(i);
            if (!StringUtils.startsWith(sheet.getSheetName(), "t_")) {
                continue;
            }
            int physicalNumberOfRows = sheet.getPhysicalNumberOfRows();
            if (physicalNumberOfRows <= 5) {
                throw new ConfigException(ExceptionEnum.SHEET_NOT_ENOUGH_VALID_ROWS, sheet.getSheetName());
            }
            SheetDefinition sheetDefinition = new SheetDefinition();
            sheetDefinition.setSheetName(MyStringUtils.capFirst(sheet.getSheetName().substring(2)));
            sheetDefinition.setFileName(fileName);
            sheetDefinition.setJavaPackage(javaPackage);
            sheetDefinition.setSheetDesc(getStringCellValue(sheet, 0, 0));

            Row row1 = sheet.getRow(1);
            Set<String> fieldNames = new HashSet<>();
            Iterator<Cell> cellIterator = row1.cellIterator();
            while (cellIterator.hasNext()) {
                Cell cell = cellIterator.next();
                String value = cell.getStringCellValue();
                if (StringUtils.isBlank(value)) {
                    continue;
                }
                if (!fieldNames.add(value)) {
                    throw new ConfigException(ExceptionEnum.REPEATED_FIELD_NAME, sheet.getSheetName(), value);
                }
                value = value.trim();
                int colNum = cell.getColumnIndex();
                FieldDefinition fieldDefinition = new FieldDefinition();
                fieldDefinition.setFieldName(value);
                fieldDefinition.setColNum(colNum);
                fieldDefinition.setFieldClass(getStringCellValue(sheet, 2, colNum));
                if (StringUtils.isBlank(fieldDefinition.getFieldClass())) {
                    throw new ConfigException(ExceptionEnum.FIELD_NO_CLASS, sheetDefinition.getFileName(), sheet.getSheetName(), value);
                }
                fieldDefinition.setKey(getStringCellValue(sheet, 3, colNum));
                fieldDefinition.setFieldDesc(getStringCellValue(sheet, 4, colNum).replace("\n", ""));
                fieldDefinition.setJavaClass(getJavaClass(fieldDefinition.getFieldClass()));
                fieldDefinition.setGoClass(getGoClass(fieldDefinition.getFieldClass()));
                sheetDefinition.getFields().add(fieldDefinition);
                if (fieldDefinition.getKey().equalsIgnoreCase("key")) {
                    sheetDefinition.setKeyColNum(colNum);
                    sheetDefinition.setKeyName(value);
                }
            }
            if (sheetDefinition.getKeyColNum() == -1) {
                throw new ConfigException(ExceptionEnum.SHEET_NOT_KEY, sheet.getSheetName());
            }
            JSONArray dataArray = new JSONArray();
            for (int rowNum = 5; rowNum < physicalNumberOfRows; rowNum++) {
                String keyValue = getStringCellValue(sheet, rowNum, sheetDefinition.getKeyColNum());
                if (keyValue.equals("")) {
                    break;
                }
                JSONObject jsonObject = new JSONObject();
                List<FieldDefinition> fields = sheetDefinition.getFields();
                for (FieldDefinition field : fields) {
                    try {
                        Object value = getCellValue(sheet, field.getFieldClass(), rowNum, field.getColNum());
                        jsonObject.put(field.getFieldName(), value);
                    } catch (Exception e) {
                        log.error("第{}行{}列字段{}出错：", rowNum, field.getColNum(), field.getFieldName());
                        throw e;
                    }

                }
                dataArray.add(jsonObject);
            }
            sheetDefinition.setJsonData(dataArray.toJSONString());
            sheetDefinition.setJsonArray(dataArray);
            list.add(sheetDefinition);
        }
        return list;

    }

    private String getGoClass(String fieldClass) {
        ICellParser cellParser = getCellParser(fieldClass);
        return cellParser.goClass();
    }

    private String getJavaClass(String fieldClass) {
        ICellParser cellParser = getCellParser(fieldClass);
        return cellParser.javaClass();
    }


    private Object getCellValue(Sheet sheet, String fieldClass, int rowNum, int colNum) {
        Cell cell = sheet.getRow(rowNum).getCell(colNum);
        ICellParser iCellParser = getCellParser(fieldClass);
        if (cell == null) {
            return iCellParser.defaultValue();
        }
        String stringCellValue = getStringCellValue(sheet, rowNum, colNum);
        return iCellParser.parseStringCellValue(stringCellValue);
    }

    private String getStringCellValue(Sheet sheet, int rowNum, int colNum) {
        Row row = sheet.getRow(rowNum);
        if (row == null) {
            return "";
        }
        Cell cell = row.getCell(colNum);
        if (cell == null) {
            return "";
        }
        switch (cell.getCellTypeEnum()) {
            case STRING:
                return cell.getStringCellValue();
            case BOOLEAN:
                return cell.getBooleanCellValue() ? "1" : "0";
            case NUMERIC:
            case FORMULA:
            case BLANK:
            default:
                cell.setCellType(CellType.STRING);
                return cell.getStringCellValue();
        }
    }
}
